define([], function(){
	var excel = function(id, name, container) {
		var a = this;
		a.id = id;
		a.name = name;
		a.maxCol = 0;
		a.cells = [];
		a.cellInfos = [];
		a.tables = {};
		a.excelPos = {};
		a.container = container;
		a.dragDiv = undefined;
		a.showEmptyCell = true;
		a.showExcelHead = true;
		return a;
	};

	excel.prototype = {
		cellInfo : function(col, width, visible) {
			this.cellInfos[col] = {
				width: width,
				visible: (width == 0? false : visible)
			}
		},
		
		cellWidth : function(col, width) {
			if (! this.cellInfos[col])
				this.cellInfos[col] = {visible: true};
			this.cellInfos[col].width = parseInt(width);
			if (this.cellInfos[col].width == 0)
				this.cellInfos[col].visible = false;
		},
		
		cellVisible : function(col, visible) {
			if (! this.cellInfos[col])
				this.cellInfos[col] = {width: 0};
			this.cellInfos[col].visible = visible;
		},
		
		/**
		 * 置入单元格数据
		 * @param row
		 * @param col
		 * @param data
		 */
		put : function(row, col, data) {
			var a = this;
			makeRows(a.cells, row + 1);
			a.cells[row][col] = data;
			a.maxCol = Math.max(a.maxCol, col + 1);
		},
		
		set : function(row, col, appendData) {
			$.extend(true, this.cells[row][col], appendData);
		},
		
		get : function(row, col) {
			var a = this;
			if (! a.cells[row])
				return undefined;
			var cell = a.cells[row][col];
			if (cell)
				return cell;
			if (col > 0) {
				// 观察左侧单元格是否为colspan
				cell = a.get(row, col-1);
				if (cell && cell.colspan)
					return cell;
			}
			if (row > 0) {
				// 观察上方单元格是否为rowspan
				cell = a.get(row-1, col);
				if (cell && cell.rowspan)
					return cell;
			}
			return undefined;
		},
		
		addTable : function(key, table) {
			this.tables[key] = table;
		},
		
		caculate : function(callback) {
			var a = this;
			// console.log(a.excelPos);
			require([ 'js/formula/igame' ], function() {
				require([ 'js/formula/operandBase', 'js/formula/operatorBase' ], function() {
					require([ 'js/formula/operandBoolean', 
							'js/formula/operandNumber', 
							'js/formula/operandString', 
							'js/formula/operandArray', 
							'js/formula/operandCell', 
							'js/formula/operandFormatter', 
							'js/formula/binaryOperator', 
							'js/formula/unaryOperator', 
							'js/formula/functionBase' ], function() {
						require(['js/formula/operatorParentheses', 
								'js/formula/operatorContinuous', 
								'js/formula/operatorAdd', 
								'js/formula/operatorSub', 
								'js/formula/operatorMul', 
								'js/formula/operatorDiv', 
								'js/formula/operatorMod', 
								'js/formula/operatorShiftLeft', 
								'js/formula/operatorShiftRight', 
								'js/formula/operatorBitwiseAnd', 
								'js/formula/operatorBitwiseOr', 
								'js/formula/operatorAnd', 
								'js/formula/operatorGreater', 
								'js/formula/operatorGreaterEqual', 
								'js/formula/operatorEqual', 
								'js/formula/operatorNotEqual',
								'js/formula/operatorLess', 
								'js/formula/operatorLessEqual', 
								'js/formula/operatorOr', 
								'js/formula/operatorXor', 
								'js/formula/operatorMinus', 
								'js/formula/operatorPlus', 
								'js/formula/operatorNot', 
								'js/formula/operatorBitwiseNot', 
								'js/formula/functionAverage', 
								'js/formula/functionSum', 
								'js/formula/functionPower', 
								'js/formula/functionText', 
								'js/formula/functionTotime', 
								'js/formula/functionIf',
								'js/formula/functionLen',
								'js/formula/functionRound', 
								'js/formula/functionFloor',
								'js/formula/functionMax',
								'js/formula/formula'], function(){
							try {
								// console.log(a.excelPos);
								igame.bind(a.excelPos, a.maxCol);
								igame.getBindData = function(e) {
									if (this.data[e].data.real !== undefined)
										return this.data[e].data.real;
									else {
										if (stack.contains(e)) {
											throw new Error('重复引用单元格：' + e);
										}
										stack.push(e);
										a.caculateOne(this.data[e], e);
										return this.data[e].data.real;
									}
								}
								
								var expression, cell, format, stack = [];
								for(var key in a.excelPos) {
									try {
										stack.push(key);
										a.caculateOne(a.excelPos[key], key);
										stack.length = 0;
									} catch(e) {
										console.error('单元格[' + key + ']公式解释异常', e);
										break;
									}
								}
								// console.info('After excel formula', a.excelPos)
							} catch(e) {
								console.error(e);
							}
							
							if ($.isFunction(callback))
								callback();
						})
					})
				})
			})
		},
		
		caculateOne : function(cell, key) {
			var expression = cell.data.value;
			if (expression !== undefined && expression != null && $.type(expression) == 'string' && expression[0] == '=' && !cell.data.exp) {
				// 处理特定公式
				//console.log(expression, key);
				var fm = new igame.Expression.Formula(expression.trim());
				//console.log(fm);
				// 进行公式计算
				var ev = fm.evaluate();
				if (ev instanceof igame.Expression.Operand.Cell)
					ev = igame.getOperand(ev.getValue());
				
				//console.log(ev);
				cell.data.value = ev.getValue();
				cell.data.real = ev.real;
				cell.data.exp = ev.excelExp;
				if (cell.dom) {
					cell.dom.text(cell.data.value);
					cell.dom.attr('title', cell.data.exp);
				}
			} else
				if (cell.data.real == undefined)
					cell.data.real = (cell.data.value == undefined ? '' : cell.data.value);
			
			// 格式化处理
			var format = cell.data.format;
			if (format && !cell.data.formated) {
				if (! cell.data.subtable)
					$F.err('Excel - caculate', '单元格未绑定subtable对象');
				else {
					cell.data.value = getFormatValue(format, cell.data.value, cell.data.subtable.replaces);
					if (cell.dom) {
						cell.dom.text(cell.data.value);
						cell.data.formated = true; // 防止跨列或跨行单元格重复格式化
					}
				}
			}
		},
			
		drawTitle : function(title) {
			// this.name = title.replaceAll(':', '');
			$('.rpt_title .js_title', this.container).text(title);
		},
		
		/*
		 * 绘制表格表头
		 */
		drawHead : function() {
			var a = this, 
				table = $('.tb_head', a.container).empty(), 
				headBox = $('.rpt_head_box', a.container).css({width: 'auto', paddingRight: '40px'});
			if (a.autoGridSize) {
				// 自动列宽，满屏
				headBox.css({'paddingRight': 0});
				table.forceClass('autogridsize');
			} else
				table.removeClass('autogridsize');

			var tr = $('<tr />').appendTo(table);
			for (col = 0; col < a.maxCol; col++) {
				td = $('<td />').appendTo(tr);
				span = $('<span>' + a.int2col(col+1) + '</span>').appendTo(td);
				if (a.cellInfos[col]) {
					if (! a.cellInfos[col].visible) {
						// 隐藏该列
						td.addClass('hide');
					} else {
						// 约定单元格宽度
						if (! a.autoGridSize)
							span.css({
								width : a.cellInfos[col].width + 'px'
							})
					}
				} else {
					if (! a.autoGridSize)
						span.css({width: '60px'})
				}
			}
		},
		
		drawGrid : function() {
			var a = this, table = $('.tb_grid', a.container).empty(), heads = [], lineno = $('.tb_lineno', a.container).empty();
			a.drawHead();
			$('.tb_head td', a.container).each(function(i, td) {
				td = $(td);
				heads.push({
					width: $('>span', td).outerWidth(),
					hidden: td.hasClass('hide')
				})
			})
			console.log('Excel heads:', heads);
			// console.log('Excel cellInfos:', a.cellInfos);
			
			if (a.autoGridSize) {
				// 自动列宽，满屏
				table.forceClass('autogridsize');
				lineno.forceClass('autogridsize');
			} else {
				table.removeClass('autogridsize');
				lineno.removeClass('autogridsize');
			}
			
			// console.log('Excel cells:', a.cells);
			var hasBorder, bgColor;
			var row, col, subtable, oe;
			var position, data;
			var tr, td, span, lineTr;
			for(row = 0; row < a.cells.length; row ++) {
				// 绘制行
				lineTr = $('<tr />').appendTo(lineno);
				tr = $('<tr />').appendTo(table);
				hasBorder = false;
				bgColor = '';
				if (subtable) {
					hasBorder = true;
					if (row < subtable.bodyStart) {
						tr.addClass('head');
						oe = 0;
						bgColor = '#f2f4ff';
					} else
						tr.addClass('line');
				} else {
					subtable = a.tables['L' + row];
					if (subtable) {
						// 该表表头背景色
						tr.addClass('head');
						oe = 0;
						hasBorder = true;
						bgColor = '#f2f4ff';
						console.log('Find subgrid:', subtable);
						
						tr.prev().addClass('dl');
					}
				}
				if (tr.hasClass('line')) {
					oe ++;
					if (oe % 2 == 0) {
						tr.addClass('oe');
						bgColor = '#f9fbfb'
					}
					tr.on('click', function(e) {
						$('.selected', table).removeClass('selected');
						$(e.currentTarget).addClass('selected');
					})
				}

				lineTr.append('<td class="fixedColumn" />');
				lineTr.append('<td class="number"><span>' + (row + 1) + '</span></td>');
				tr.append('<td class="fixedColumn" />');
				for (col = 0; col < a.maxCol; col++) {
					// 绘制列
					position = a.int2col(col + 1) + (row + 1);
					data = a.cells[row][col];
					span = false;
					if (! data) {
						data = {
							position: position,
							border: hasBorder,
							bgColor: bgColor,
							value: ''
						}
						a.cells[row][col] = data;
						
						td = $('<td />').appendTo(tr);
						span = $('<span>&nbsp;</span>').appendTo(td);
						if (heads[col].hidden)
							// 隐藏该列
							td.addClass('hide');
						else {
							if (! a.autoGridSize)
								span.css({
									width : heads[col].width + 'px'
								})
						}
					} else {
						data.border = hasBorder;
						data.position = position;
						data.bgColor = bgColor;
						if (data.occupied) {
							// console.log('发现合并的列单元格:' + position);
							// 行列合并后的占位单元
							if (data.colpos > 1) {
								// 占位单元为列合并
								var xyc = a.excelPos[a.int2col(col - data.colpos + 2) + (row + 1)]; // 扩展的目标单元格
								if (! xyc)
									throw new Error('合并行的扩展单元格未定义，' + position + ' quote ' + (row + 1) + a.int2col(col - data.colpos + 2));
								span = xyc.dom;
								data.value = span.data('data').value;
								if (! heads[col].hidden) {
									td.removeClass('hide');
								}
								span.css({
									width : '100%',
									textAlign : 'center'
								})
							} 
							if (data.rowpos > 1) {
								// 占位单元为行合并
								var xyc = a.excelPos[a.int2col(col + 1) + (row - data.rowpos + 2)]; // 扩展的目标单元格
								if (! xyc)
									throw new Error('合并行的扩展单元格未定义，'+ position + ' quote ' + ref);
								span = xyc.dom;
								data.value = span.data('data').value;
							}
						} else {
							data.subtable = subtable;
							if (subtable && subtable.headFont && tr.hasClass('head')) {
								data.fontFamily = subtable.headFont.name;
								data.fontSize = subtable.headFont.size;
								data.fontBold = subtable.headFont.bold;
								data.fontItalic = subtable.headFont.italic
								data.color = subtable.headFont.color;
							}
							if (subtable && subtable.font && tr.hasClass('line')) {
								data.fontFamily = subtable.font.name;
								data.fontSize = subtable.font.size;
								data.fontBold = subtable.font.bold;
								data.fontItalic = subtable.font.italic
								data.color = subtable.font.color;
							}

							td = $('<td />').appendTo(tr);
							if (row == 0) {
								td.attr('colspan', a.maxCol);
								tr.addClass('title')
							}
							if (data.colspan > 1) {
								//console.log(data);
								var colspan = data.colspan, k = col + colspan;
								for(var i=col; i<k; i++) {
									if (i >= a.cellInfos.length) {
										colspan --;
										console.error('合并列在超出表格总列定义数: colspan from ' + col + ' to ' + k + ', max col ' + a.cellInfos.length);
									} else if (! a.cellInfos[i].visible)
										colspan --;
								}
								td.prop('colspan', colspan);
							}
							if (data.rowspan > 1)
								td.prop('rowspan', data.rowspan);
							
							span = $('<span>' + notNullValue(data.value) + '</span>')
								.appendTo(td)
								.data('data', data)
								.attr('title', notNullValue(data.value))
								.click(function(e) {
									//console.log($(e.currentTarget).data('data'))
								});
							if (data.fontFamily)
								span.css({
									fontFamily: data.fontFamily
								});
							if (data.fontSize)
								span.css({
									fontSize: data.fontSize + 'pt'
								});
							if (data.fontItalic)
								span.css({
									fontStyle: 'italic'
								})
							if (data.fontBold)
								span.css({
									fontWeight: 'bold'
								})
							if (data.color)
								span.css({
									color: '#' + data.color
								});
							if (data.height) {
								$('.fixedColumn', lineTr).css({
									height: data.height + 'pt'
								})
								$('.fixedColumn', tr).css({
									height: data.height + 'pt'
								});
								if (a.autoGridSize) {
									span.css({
										lineHeight: data.height + 'pt'
									})
									tr.css({
										height: data.height + 'pt'
									})
									lineTr.css({
										height: data.height + 'pt'
									})
								}
							}
							
							if (heads[col].hidden) {
								// 隐藏该列
								td.addClass('hide');
							}
							else {
								if (! a.autoGridSize)
									span.css({
										width : heads[col].width + 'px'
									})
							}
							if (data.align) {
								// 单元格单独指定了对齐方式
								span.css('textAlign', data.align == 3?'right':(data.align == 2?'center':'left'))
							}

							if (data.overflow)
								span.css('overflow', data.overflow);	// 非子表格的单元格使用扩展显示
							
						}
					}
					if (! span)
						throw new Error('数据表格绘制异常，创建单元数据SPAN对象失败');
					
					a.excelPos[position] = {
						dom: span,
						data: data
					}
				}

				if (subtable && row >= subtable.end - 1)
					subtable = false;
			}
			
			table.off('resize').on('resize', function(){
				if (a.autoGridSize)
					$('.rpt_head_box', a.container).css({
						width: table.width() + 'px'
					})
			}).trigger('resize');
			
			$('.rpt_grid_container').off('scroll').on('scroll', function(e){
				$('.rpt_head', a.container)[0].scrollLeft = e.target.scrollLeft;
				$('.rpt_lineno', a.container)[0].scrollTop = e.target.scrollTop;

				$('.dragDiv', a.container).css({
					left: (e.target.scrollLeft * -1) + 'px'
				})
				rePosDrag(a);
			})
		},
		
		drawDrag : function() {
			var a = this, drag;
			a.dragDiv = $('.dragDiv', a.container).empty().html('');
			if (a.autoGridSize)
				return;
			
			$('.tb_head tr:eq(0)>td', a.container).each(function(i, v){
				drag = $('<div data-col="' + i + '" />').css({
					height : '100%',
					display : "block",
				})
				.appendTo(a.dragDiv)
				.data('col', i)
				.mousedown(function(e) {
					dragStart(a, e, this);
				});
				if ($(this).hasClass('hide')) {
					// console.info('Detected hidden column: ' + i);
					drag.hide();
				}
			})
			
			$(document).mousemove(function(e) {
				dragMove(a, e);
			}).mouseup(function(e) {
				dragEnd(a, e);
			}).hover(function(e) {
			}, function(e) {
				dragEnd(a, e);
			});

			rePosDrag(a);
		},

		/**
		 * 转换excel列为数字，A=1
		 * @method col2int
		 * @param col {string} excel表格的列号字母
		 * @return {integer}
		 */
		col2int : function(col) {
			var map = [];
			var codeA = "A".charCodeAt();

			col = col.toUpperCase();
			var power = 0;
			var result = 0;
			for (var i = col.length - 1; i >= 0; i--, power++) {
				var ch = col[i];
				result += (ch.charCodeAt() - codeA + 1) * Math.pow(26, power);
			}

			return result;
		},

		/**
		 * 转换数字为excel列，1=A
		 * @method int2col
		 * @param col {integer} 二维数组下标
		 * @return {string}
		 */
		int2col : function(col) {
			var res = "";
			var codeA = "A".charCodeAt();
			while (col > 0) {
				col--;
				res = String.fromCharCode(codeA + col % 26) + res;
				col = parseInt(col / 26);
			}
			return res;
		}
	};
	
	
	var makeRows = function(array, length) {
		while (array.length < length) {
			array.push([]);
		}
	},
	
	rePosDrag = function(a) {
		if (a.autoGridSize)
			return;
		
		var th, cdpos, cdleft = 0;
		$('.tb_head tr:eq(0)>td', a.container).each(function(i, v) {
			th = $(v);
			if (th.hasClass('hide')) {
				$('div:eq(' + i + ')', a.dragDiv).css({
					left : 0
				});
				return true;
			}

			cdpos = th.outerWidth() + cdleft;
			$('div:eq(' + i + ')', a.dragDiv).css({
				left : (cdpos - 1) + 'px'
			}).data('width', th.width());
			cdleft = cdpos;
		});
	},
	
	dragStart = function(a, e, obj) {
		obj = $(obj).addClass('dragging');
		obj.prev().addClass('dragging');
		a.colResize = {
			startX : e.pageX,
			ol : parseInt(obj.css('left')),
			ow : obj.data('width'),
			n : obj.data('col')
		};
		$('body').css('cursor', 'col-resize');
		$('body').noSelect();
		// console.log(a.colResize);
	},
	
	dragMove = function(a, e) {
		if (a.colResize) {
			var n = a.colResize.n,
				diff = e.pageX - a.colResize.startX,
				nleft = a.colResize.ol + diff,
				nw = a.colResize.ow + diff;
			if (nw > 0) {
				$('div:eq(' + n + ')', a.dragDiv).css('left', nleft);
				a.colResize.diff = diff;
				a.colResize.nw = nw;
			}
		}
	},
	
	dragEnd = function(a, e) {
		if (a.colResize) {
			// console.log(a.colResize);
			$F.process(function(){
				try {
					var table = $('.tb_grid', a.container), xyc, pos;
					var headSpan = $('.tb_head tr:eq(0)>td:eq(' + a.colResize.n + ')>span', a.container);
					headSpan.css({
						width: (headSpan.outerWidth() + a.colResize.diff) + 'px'
					})
					$('tr', table).each(function(i, tr) {
						// 跨行单元格，由于同一个span被存放在多个不同POSITION中，
						// 因此需要放置重复宽度设置操作
						pos = a.int2col(a.colResize.n + 1) + (i + 1);
						xyc = a.excelPos[pos];
						if (xyc) { // fixed for rowspan, none span in excelPos
							if (xyc.data.rowpos === undefined || xyc.data.rowpos < 2) {
								if (xyc.data.colspan > 1) {
									//console.log('跨列: ' + pos, xyc.data);
								} else {
									xyc.dom.css({
										width: (xyc.dom.outerWidth() + a.colResize.diff) + 'px'
									});
								}
							} else {
								//console.log('跨行: ' + pos)
							}
						} else {
							//console.log(a.excelPos);
							console.error('Missing xy cell entity： ' + pos)
						}
					})
				} catch(e) {
					console.error(e);
				}
				$('.dragging', a.dragDiv).removeClass('dragging');
				rePosDrag(a);
				a.colResize = false;
			}, 'Resizing...')
		}
		$('body').css('cursor', 'default');
		$('body').noSelect(false);
	},
	
	notNullValue = function(value) {
		if (value == undefined || value == null)
			return '';
		else
			return value;
	},
	
	/**
	 * 对数据进行格式化处理
	 */
	getFormatValue = function(format, value, replaces) {
		if (format.toLowerCase() == 'totime')
			return long2time(value);
		
		if (format.toLowerCase() == 'only')
			return value;
		
		if (value == '' || value == undefined)
			return value;
		
		// 判断是否替换项目
		if (Verify.isDigit(format)) {
			var col = parseInt(format);
			if (replaces == undefined) {
				$F.err('Excel - getFormatValue', '未定义的替换项目：' + format);
			} else {
				if (replaces.length < col) {
					$F.err('Excel - getFormatValue', '替换项目列错误：' + format, replaces);
				} else {
					if (value || value === 0) {
						// console.log('replace ' + value + ' to ' + replaces[col][value + '']);
						let v = replaces[col][value + ''];
						if (v !== undefined)
							return v;
					}
				}
			}
		} else {
			if ($F.isDate(value))
				return $F.parseDate(value).format(format);
		}
		return value;
	},
	
	long2time = function(value) {
		var seconds = parseInt(value);// 秒
		if (isNaN(seconds))
			return '';
		
		var minites = 0;// 分
		var hours = 0;// 小时
		
		minites = parseInt(seconds / 60);
		seconds = parseInt(seconds % 60);	// 余秒
		hours = parseInt(minites / 60);
		minites = parseInt(minites % 60);	// 余分
		
		var hoursStr = hours > 9 ? hours : ('0' + hours);
		var minitesStr = minites > 9 ? minites : ('0' + minites);
		var seconsStr = seconds > 9 ? seconds : ('0' + seconds);
		return hoursStr + ':' + minitesStr + ':' + seconsStr;
	};

	return {
		create: function(id, name, container) {
			return new excel(id, name, container);
		}
	}
});

